package co.baiku.boot.core.orm.service.impl;

import co.baiku.boot.common.exception.ApiException;
import co.baiku.boot.common.message.PageData;
import co.baiku.boot.common.spring.SpringContext;
import co.baiku.boot.common.tools.GenericsTools;
import co.baiku.boot.common.tools.ObjectTools;
import co.baiku.boot.core.orm.dao.BaseDAO;
import co.baiku.boot.core.orm.dto.mapper.IMapper;
import co.baiku.boot.core.orm.po.Po;
import co.baiku.boot.core.orm.request.PagingRequest;
import co.baiku.boot.core.orm.request.SearchGroup;
import co.baiku.boot.core.orm.service.BaseService;
import co.baiku.boot.core.orm.tools.QueryTools;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class BaseServiceImpl<T extends Po, D, ID extends Serializable> implements BaseService<T, D, ID>, BaseDAO<T, ID> {
    protected T entity;

    protected abstract BaseDAO<T, ID> getBaseDAO();

    public BaseServiceImpl() {
        this.entity = instanceAnnotationObject();
    }

    private Map<String, ColumnCache> getColumnMap() {
        return LambdaUtils.getColumnMap(this.entity.getClass());
    }

    @SuppressWarnings("unchecked")
    protected T instanceAnnotationObject() {
        try {
            return (T) GenericsTools.getSuperClassGenricType(getClass(), 0)
                    .newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public T saveOrUpdate(T entity) {
        if (entity.getId() != null) {
            getBaseDAO().updateById(entity);
        } else {
            getBaseDAO().insert(entity);
        }
        return entity;
    }

    @Override
    public void delete(ID id) {
        T entity = getBaseDAO().selectById(id);
        if (entity != null) {
            delete(entity);
        }
    }

    @Override
    public void delete(Collection<ID> ids) {
        List<T> entities = getBaseDAO().selectBatchIds(ids);
        if (ObjectTools.isNotBlank(entities)) {
            deleteAll(entities);
        }
    }

    @Override
    public T getById(ID id) {
        return getBaseDAO().selectById(id);
    }

    @Override
    public D getDtoById(ID id) {
        return toDto(getById(id));
    }

    @Override
    public PageData<D> findDtoPaging(Integer page, Integer rows, Map<String, Object> searchMap, Map<String, String> sort) {
        Page<T> selectPage = getBaseDAO().selectPage(new Page<T>(page, rows), QueryTools.parseRequest(getColumnMap(), searchMap, sort));
        return PageData.of((int) selectPage.getCurrent(), (int) selectPage.getSize(), (int) selectPage.getTotal(), toDto(selectPage.getRecords()));
    }

    @Override
    public PageData<D> findDtoPaging(Integer page, Integer rows, Map<String, Object> searchParams, List<SearchGroup> searchGroups, Map<String, String> sort) {
        return findDtoPaging(page, rows, searchParams, sort);
    }

    @Override
    public PageData<D> findDtoPaging(PagingRequest pagingRequest) {
        return findDtoPaging(pagingRequest.getPageNo(), pagingRequest.getPageSize(), pagingRequest.getSearch(), pagingRequest.getSortBy());
    }

    @Override
    public PageData<T> findPaging(PagingRequest pagingRequest) {
        Page<T> selectPage = getBaseDAO().selectPage(new Page<T>(pagingRequest.getPageNo(), pagingRequest.getPageSize()), QueryTools.parseRequest(getColumnMap(), pagingRequest.getSearch(), pagingRequest.getSortBy()));
        return PageData.of((int) selectPage.getCurrent(), (int) selectPage.getSize(), (int) selectPage.getTotal(), selectPage.getRecords());
    }

    @Override
    public List<T> findAll() {
        return getBaseDAO().selectList(new QueryWrapper<>());
    }

    @Override
    public List<T> findAll(Map<String, Object> searchParams) {
        return getBaseDAO().selectList(QueryTools.parseRequest(getColumnMap(), searchParams, null));
    }

    @Override
    public List<T> findAll(Map<String, Object> searchParams, Map<String, String> sort) {
        return getBaseDAO().selectList(QueryTools.parseRequest(getColumnMap(), searchParams, sort));
    }

    @Override
    public List<T> findByProperty(String property, Object value) {
        return findByProperty(property, value, null);
    }

    @Override
    public List<T> findByProperty(String property, Object value, Map<String, String> sort) {
        Map<String, Object> search = new HashMap<>();
        search.put(property, value);
        return findAll(search, sort);
    }


    @Override
    public D toDto(T entity) {
        return (D) mapper().toDto(entity);
    }

    @Override
    public List<D> toDto(List<T> entity) {
        return mapper().toDto(entity);
    }

    @Override
    public PageData<D> toDto(PageData<T> page) {
        return PageData.of(page.getPageNo(), page.getPageSize(), page.getTotalCount(), toDto(page.getData()));
    }

    @Override
    public T toPo(D dto) {
        return (T) mapper().toPo(dto);
    }

    @Override
    public List<T> toPo(List<D> dtos) {
        return mapper().toPo(dtos);
    }

    @Override
    public void delete(T entity) {
        getBaseDAO().deleteById(entity.getId());
    }

    @Override
    public void deleteAll(Collection<T> entity) {
        if (ObjectTools.isNotBlank(entity)) {
            for (T t : entity) {
                delete(t);
            }
        }
    }

    @Override
    public List<T> findByIdIn(Collection<ID> ids) {
        return selectBatchIds(ids);
    }

    @Override
    public int batchInsert(Collection<T> entites) {
        return getBaseDAO().batchInsert(entites);
    }

    @Override
    public int batchUpdate(Collection<T> entites) {
        return getBaseDAO().batchUpdate(entites);
    }

    @Override
    public int insert(T entity) {
        return getBaseDAO().insert(entity);
    }

    @Override
    public int deleteById(Serializable id) {
        return getBaseDAO().deleteById(id);
    }

    @Override
    public int deleteByMap(Map<String, Object> columnMap) {
        return getBaseDAO().deleteByMap(columnMap);
    }

    @Override
    public int delete(Wrapper<T> queryWrapper) {
        return getBaseDAO().delete(queryWrapper);
    }

    @Override
    public int deleteBatchIds(Collection<? extends Serializable> idList) {
        return getBaseDAO().deleteBatchIds(idList);
    }

    @Override
    public int updateById(T entity) {
        return getBaseDAO().updateById(entity);
    }

    @Override
    public int update(T entity, Wrapper<T> updateWrapper) {
        return getBaseDAO().update(entity, updateWrapper);
    }

    @Override
    public T selectById(Serializable id) {
        return getBaseDAO().selectById(id);
    }

    @Override
    public List<T> selectBatchIds(Collection<? extends Serializable> idList) {
        return getBaseDAO().selectBatchIds(idList);
    }

    @Override
    public List<T> selectByMap(Map<String, Object> columnMap) {
        return getBaseDAO().selectByMap(columnMap);
    }

    @Override
    public T selectOne(Wrapper<T> queryWrapper) {
        return getBaseDAO().selectOne(queryWrapper);
    }

    @Override
    public Integer selectCount(Wrapper<T> queryWrapper) {
        return getBaseDAO().selectCount(queryWrapper);
    }

    @Override
    public List<T> selectList(Wrapper<T> queryWrapper) {
        return getBaseDAO().selectList(queryWrapper);
    }

    @Override
    public List<Map<String, Object>> selectMaps(Wrapper<T> queryWrapper) {
        return getBaseDAO().selectMaps(queryWrapper);
    }

    @Override
    public List<Object> selectObjs(Wrapper<T> queryWrapper) {
        return getBaseDAO().selectObjs(queryWrapper);
    }

    @Override
    public <E extends IPage<T>> E selectPage(E page, Wrapper<T> queryWrapper) {
        return getBaseDAO().selectPage(page, queryWrapper);
    }

    @Override
    public <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, Wrapper<T> queryWrapper) {
        return getBaseDAO().selectMapsPage(page, queryWrapper);
    }

    @Override
    public IMapper<T, D> mapper() {
        String simpleName = entity.getClass().getSimpleName();
        String mapper = simpleName + "MapperImpl";
        IMapper<T, D> mapperImpl = SpringContext.getIfPresent(mapper.substring(0, 1).toLowerCase() + mapper.substring(1));
        if (mapperImpl == null) {
            throw new ApiException("没有创建" + simpleName + "Mapper");
        }
        return mapperImpl;
    }

    @Override
    public void deleteByProperty(String property, Object value) {
        Map<String, Object> searchMap = new HashMap<>(1);
        searchMap.put(property, value);
        delete(QueryTools.parseRequest(getColumnMap(), searchMap, null));
    }

    @Override
    public void deleteByParams(Map<String, Object> searchParams) {
        delete(QueryTools.parseRequest(getColumnMap(), searchParams, null));
    }
}
